using System;
using System.Linq;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using Microsoft.IO;

namespace Framework.Network
{
    public sealed class TcpService : BaseService
    {
        private readonly Dictionary<int, TcpChannel> m_Id2Channel = new Dictionary<int, TcpChannel>();

        private readonly SocketAsyncEventArgs m_InnerArgs = new SocketAsyncEventArgs();

        private Socket m_Acceptor;

        private HashSet<int> m_NeedStartSendChannel = new HashSet<int>();

        public int PacketSizeLength { get; }

        public RecyclableMemoryStreamManager MemoryStreamManager = new RecyclableMemoryStreamManager();

        // public TcpService(int packetSizeLength, IPEndPoint ipEndPoint, Action<BaseChannel> acceptCallBack)
        // {
        //     this.PacketSizeLength = packetSizeLength;
        //     this.AcceptCallBack += acceptCallBack;

        //     this.m_Acceptor = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //     this.m_Acceptor.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        //     this.m_Acceptor.Bind(ipEndPoint);
        //     this.m_Acceptor.Listen(1000);

        //     this.m_InnerArgs.Completed += this.OnComplete;
        // }

        public TcpService(int packetSizeLength) : base()
        {
            this.PacketSizeLength = packetSizeLength;
        }

        public override void Dispose()
        {
            if (this.IsDisposed)
            {
                return;
            }
            base.Dispose();
            foreach (int id in this.m_Id2Channel.Keys.ToArray())
            {
                TcpChannel channel = this.m_Id2Channel[id];
                channel.Dispose(); 
            }
            this.m_Acceptor?.Close();
            this.m_Acceptor = null;
            this.m_InnerArgs.Dispose();
        }

        public override BaseChannel GetChannel(int channelId)
        {
            TcpChannel channel = null;
            this.m_Id2Channel.TryGetValue(channelId, out channel);
            return channel;
        }

        public override BaseChannel ConnectChannel(IPEndPoint ipEndPoint)
        {
            TcpChannel channel = new TcpChannel(ipEndPoint, this);
            this.m_Id2Channel[channel.Id] = channel;
            return channel;
        }

        public override BaseChannel ConnectChannel(string address)
        {
            IPEndPoint ipEndPoint = NetworkHelper.ToIPEndPoint(address);
            return ConnectChannel(ipEndPoint);
        }

        public override void Remove(int channelId)
        {
            TcpChannel channel;
            if (!this.m_Id2Channel.TryGetValue(channelId, out channel))
            {
                return;
            }
            if (channel == null)
            {
                return;
            }
            this.m_Id2Channel.Remove(channelId);
            channel.Dispose();
        }

        public void MarkNeedStartNeed(int id)
        {
            this.m_NeedStartSendChannel.Add(id);
        }

        public void Update()
        {
            foreach (int id in this.m_NeedStartSendChannel)
            {
                TcpChannel channel;
                if (!this.m_Id2Channel.TryGetValue(id, out channel))
                {
                    continue;
                }
                if (channel.IsSending)
                {
                    continue;
                }
                try
                {
                    channel.StartSend();
                }
                catch (Exception e)
                {
                    Log.Error(e);
                }
            }
            this.m_NeedStartSendChannel.Clear();
        }

        public void AcceptAsync()
        {
            this.m_InnerArgs.AcceptSocket = null;
            if (this.m_Acceptor.AcceptAsync(this.m_InnerArgs))
            {
                return;
            }
            this.OnAcceptComplete(this.m_InnerArgs);
        }

        private void OnAcceptComplete(object o)
        {
            if (this.m_Acceptor == null)
            {
                return;
            }
        }

        private void OnComplete(object sender, SocketAsyncEventArgs e)
        {
            Log.Info("TcpService OnComplete");  
        }

    }
}